import Foundation
import Combine
import SwiftUI

// MARK: - Data Structures

struct GoalAchievementData {
    var date: Date
    var percentage: Double
}

struct DayData {
    var date: Date
    var amount: Double
}

enum StatsPeriod: CaseIterable {
    case day, week, month
    
    var displayName: String {
        switch self {
        case .day: return "日"
        case .week: return "周"
        case .month: return "月"
        }
    }
}

// MARK: - ViewModel

class StatisticsViewModel: ObservableObject {
    // MARK: - Published Properties
    @Published var waterIntakeStore = WaterIntakeStore()
    @Published var userProfile = UserProfile()
    @Published var selectedPeriod: StatsPeriod = .day
    @Published var chartData: [(date: Date, amount: Double)] = []
    @Published var averageIntake: Double = 0
    @Published var goalAchievementRate: Double = 0
    @Published var streakDays: Int = 0
    @Published var goalAchievementData: [GoalAchievementData] = []
    @Published var daysData: [DayData] = []
    
    // MARK: - Private Properties
    private var cancellables = Set<AnyCancellable>()
    private let calendar = Calendar.current
    
    // MARK: - Initialization
    init() {
        setupBindings()
        updateData()
    }
    
    // MARK: - Setup
    private func setupBindings() {
        // 当选定的时间段或摄入数据变化时更新图表数据
        Publishers.CombineLatest(waterIntakeStore.$intakes, $selectedPeriod)
            .map { [weak self] intakes, period in
                self?.generateChartData(intakes: intakes, period: period) ?? []
            }
            .assign(to: \.chartData, on: self)
            .store(in: &cancellables)
        
        // 计算平均摄入量
        $chartData
            .map { [weak self] data in
                guard let self = self, !data.isEmpty else { return 0 }
                return self.calculateAverage(from: data.map { $0.amount })
            }
            .assign(to: \.averageIntake, on: self)
            .store(in: &cancellables)
            
        // 计算目标达成率
        Publishers.CombineLatest($chartData, waterIntakeStore.$intakes)
            .map { [weak self] data, intakes in
                self?.calculateGoalAchievementRate(chartData: data, intakes: intakes) ?? 0
            }
            .assign(to: \.goalAchievementRate, on: self)
            .store(in: &cancellables)
    }
    
    // MARK: - Data Generation
    
    /// 根据选定的时间段生成图表数据
    private func generateChartData(intakes: [WaterIntake], period: StatsPeriod) -> [(date: Date, amount: Double)] {
        let today = calendar.startOfDay(for: Date())
        
        switch period {
        case .day:
            return generateHourlyData(for: today, intakes: intakes)
        case .week:
            return generateDailyData(for: today, days: 7, intakes: intakes)
        case .month:
            return generateMonthlyData(for: today, intakes: intakes)
        }
    }
    
    /// 生成每小时数据
    private func generateHourlyData(for date: Date, intakes: [WaterIntake]) -> [(Date, Double)] {
        return (0..<24).map { hour in
            guard let hourDate = calendar.date(bySettingHour: hour, minute: 0, second: 0, of: date),
                  let nextHour = calendar.date(byAdding: .hour, value: 1, to: hourDate) else {
                return (date, 0)
            }
            
            let hourlyAmount = intakes
                .filter { $0.date >= hourDate && $0.date < nextHour }
                .reduce(0) { $0 + $1.amount }
            
            return (hourDate, hourlyAmount)
        }
    }
    
    /// 生成每日数据
    private func generateDailyData(for date: Date, days: Int, intakes: [WaterIntake]) -> [(Date, Double)] {
        return (-days+1...0).compactMap { dayOffset in
            guard let date = calendar.date(byAdding: .day, value: dayOffset, to: date),
                  let nextDay = calendar.date(byAdding: .day, value: 1, to: date) else {
                return nil
            }
            
            let dailyAmount = intakes
                .filter { $0.date >= date && $0.date < nextDay }
                .reduce(0) { $0 + $1.amount }
            
            return (date, dailyAmount)
        }
    }
    
    /// 生成每月数据
    private func generateMonthlyData(for date: Date, intakes: [WaterIntake]) -> [(Date, Double)] {
        guard let daysInMonth = calendar.range(of: .day, in: .month, for: date)?.count else {
            return []
        }
        
        return (1...daysInMonth).compactMap { day in
            guard let date = calendar.date(from: calendar.dateComponents([.year, .month], from: date).setting(day: day)),
                  let nextDay = calendar.date(byAdding: .day, value: 1, to: date) else {
                return nil
            }
            
            let dailyAmount = intakes
                .filter { $0.date >= date && $0.date < nextDay }
                .reduce(0) { $0 + $1.amount }
            
            return (date, dailyAmount)
        }
    }
    
    /// 生成目标达成数据
    private func generateGoalAchievementData() {
        let today = calendar.startOfDay(for: Date())
        
        goalAchievementData = (-6...0).compactMap { dayOffset in
            guard let date = calendar.date(byAdding: .day, value: dayOffset, to: today),
                  let dayStart = calendar.startOfDay(for: date),
                  let nextDay = calendar.date(byAdding: .day, value: 1, to: dayStart) else {
                return nil
            }
            
            let dayIntakes = waterIntakeStore.intakes.filter { $0.date >= dayStart && $0.date < nextDay }
            let totalAmount = dayIntakes.reduce(0) { $0 + $1.amount }
            let goal = dayIntakes.first?.goal ?? userProfile.goal
            
            guard goal > 0 else { return nil }
            
            return GoalAchievementData(date: date, percentage: min(totalAmount / goal, 1.0))
        }
    }
    
    /// 生成近期每天的数据
    private func generateDaysData() {
        let today = calendar.startOfDay(for: Date())
        
        daysData = (-13...0).compactMap { dayOffset in
            guard let date = calendar.date(byAdding: .day, value: dayOffset, to: today),
                  let dayStart = calendar.startOfDay(for: date),
                  let nextDay = calendar.date(byAdding: .day, value: 1, to: dayStart) else {
                return nil
            }
            
            let totalAmount = waterIntakeStore.intakes
                .filter { $0.date >= dayStart && $0.date < nextDay }
                .reduce(0) { $0 + $1.amount }
            
            return totalAmount > 0 ? DayData(date: date, amount: totalAmount) : nil
        }
    }
    
    // MARK: - Calculations
    
    /// 计算平均值
    private func calculateAverage(from values: [Double]) -> Double {
        guard !values.isEmpty else { return 0 }
        return values.reduce(0, +) / Double(values.count)
    }
    
    /// 计算目标达成率
    private func calculateGoalAchievementRate(chartData: [(date: Date, amount: Double)], intakes: [WaterIntake]) -> Double {
        guard !chartData.isEmpty, !intakes.isEmpty else { return 0 }
        
        let goalDays = intakes.reduce(into: [Date: (amount: Double, goal: Double)]()) { result, intake in
            let date = calendar.startOfDay(for: intake.date)
            result[date] = (
                amount: (result[date]?.amount ?? 0) + intake.amount,
                goal: intake.goal
            )
        }
        
        let achievedDays = goalDays.filter { date, data in
            chartData.contains { calendar.isDate($0.date, inSameDayAs: date) } && data.amount >= data.goal
        }
        
        return Double(achievedDays.count) / Double(goalDays.count)
    }
    
    /// 计算连续达标天数
    private func calculateStreakDays() {
        let today = calendar.startOfDay(for: Date())
        let groupedByDay = Dictionary(grouping: waterIntakeStore.intakes) { calendar.startOfDay(for: $0.date) }
        
        var currentStreak = 0
        var currentDate = today
        
        while let dayRecords = groupedByDay[currentDate], !dayRecords.isEmpty {
            let dayTotal = dayRecords.reduce(0) { $0 + $1.amount }
            let dayGoal = dayRecords.first?.goal ?? userProfile.goal
            
            guard dayTotal >= dayGoal else { break }
            
            currentStreak += 1
            guard let previousDay = calendar.date(byAdding: .day, value: -1, to: currentDate) else { break }
            
            // 限制最多检查30天
            if let thirtyDaysAgo = calendar.date(byAdding: .day, value: -30, to: today),
               previousDay < thirtyDaysAgo {
                break
            }
            
            currentDate = previousDay
        }
        
        streakDays = currentStreak
    }
    
    // MARK: - Public Methods
    
    /// 更新所有统计数据
    func updateData() {
        generateGoalAchievementData()
        generateDaysData()
        calculateStreakDays()
    }
    
    /// 导出数据为CSV格式
    func exportCSVData() -> String {
        var csvString = "日期,饮水量(毫升),目标(毫升)\n"
        let dateFormatter = DateFormatter()
        dateFormatter.dateStyle = .short
        
        let dailyData = waterIntakeStore.intakes.reduce(into: [Date: (amount: Double, goal: Double)]()) { result, intake in
            let date = calendar.startOfDay(for: intake.date)
            result[date] = (
                amount: (result[date]?.amount ?? 0) + intake.amount,
                goal: intake.goal
            )
        }
        
        for (date, data) in dailyData.sorted(by: { $0.key < $1.key }) {
            csvString += "\(dateFormatter.string(from: date)),\(data.amount),\(data.goal)\n"
        }
        
        return csvString
    }
    
    // MARK: - Statistics Accessors
    
    func getTotalAmount() -> Double {
        switch selectedPeriod {
        case .day:
            return chartData.reduce(0) { $0 + $1.amount }
        case .week, .month:
            return chartData
                .filter { !calendar.isDateInToday($0.date) }
                .reduce(0) { $0 + $1.amount }
        }
    }
    
    func getAverageAmount() -> String {
        let average: Double
        switch selectedPeriod {
        case .day:
            average = averageIntake
        case .week:
            average = getTotalAmount() / 7
        case .month:
            if let daysInMonth = calendar.range(of: .day, in: .month, for: Date())?.count {
                average = getTotalAmount() / Double(daysInMonth)
            } else {
                average = 0
            }
        }
        return String(format: "%.0f", average)
    }
    
    func getTotalIntakeCount() -> Int {
        return waterIntakeStore.intakes.count
    }
    
    func getAveragePerIntake() -> String {
        let count = getTotalIntakeCount()
        guard count > 0 else { return "0" }
        
        let average = waterIntakeStore.intakes.reduce(0) { $0 + $1.amount } / Double(count)
        return String(format: "%.0f", average)
    }
    
    func getMaxIntakeDay() -> String {
        let groupedByDay = Dictionary(grouping: waterIntakeStore.intakes) { calendar.startOfDay(for: $0.date) }
        let dailyTotals = groupedByDay.mapValues { $0.reduce(0) { $0 + $1.amount } }
        
        guard let maxDay = dailyTotals.max(by: { $0.value < $1.value })?.key else {
            return "无数据"
        }
        
        let formatter = DateFormatter()
        formatter.dateFormat = "MM-dd"
        return formatter.string(from: maxDay)
    }
    
    func getRecommendedAmount() -> Double {
        return userProfile.goal
    }
    
    func getHydrationStatus() -> String {
        switch goalAchievementRate {
        case 0.9...: return "优秀"
        case 0.7..<0.9: return "良好"
        case 0.5..<0.7: return "一般"
        default: return "不足"
        }
    }
    
    func getHydrationStatusColor() -> Color {
        switch goalAchievementRate {
        case 0.9...: return .green
        case 0.7..<0.9: return .blue
        case 0.5..<0.7: return .orange
        default: return .red
        }
    }
    
    // MARK: - Sample Data
    
    func getHealthSuggestions() -> [String] {
        [
            "每天早晨空腹饮用一杯温水，可以加速新陈代谢",
            "建议在两餐之间适量饮水，避免餐中大量饮水",
            "运动前30分钟建议饮用300-500ml水",
            "尽量避免在睡前两小时大量饮水，以免影响睡眠",
            "饮水速度不宜过快，小口慢饮更有利于身体吸收"
        ]
    }
    
    func getMorningPercentage() -> Double { 0.35 }
    func getAfternoonPercentage() -> Double { 0.45 }
    func getEveningPercentage() -> Double { 0.20 }
    func getBestDrinkingTime() -> String { "8:00 - 10:00" }
}